﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using AdAggregator.Parser;
using AdAggregator.Util;
using AdAggregator.Website;
using CsQuery;
using RealEstateAggregator.Models.SearchData;
using RealEstateAggregator.Utils;
using log4net;

namespace RealEstateAggregator.Models.Parsers
{
    public class DomiportaPlParser : BaseParser<DomiportaPlSearchData, MyAdvertisement>
    {
        private readonly ILog log = LogManager.GetLogger(typeof(DomiportaPlParser));

        private const string LinkPrefix = "http://www.domiporta.pl";

        protected override String ComposeUri()
        {
            var uri = SearchData.MarketType == MarketType.Primary ? ComposePrimaryMarketUri() : ComposeSecondaryMarketUri();
            return uri;
        }

        private string ComposePrimaryMarketUri()
        {
            const string primaryMarketUri =
                "http://www.domiporta.pl/nowe/{13}/{0}?Localization={1}&PriceFrom={2}&PriceTo={3}&AreaFrom={4}&AreaTo={5}&PricePerMeterFrom={6}&PricePerMeterTo={7}&ConcatenatedFields={8}&RoomCountFrom={9}&RoomCountTo={10}&FloorNumberFrom={11}&FloorNumberTo={12}";

            var uri = String.Format(primaryMarketUri, SearchData.Page, SearchData.Localization.Replace(",", ""),
                SearchData.PriceMin, SearchData.PriceMax, SearchData.AreaMin, SearchData.AreaMax, SearchData.PricePerMeterMin,
                SearchData.PricePerMeterMax, SearchData.Keywords, SearchData.RoomCountMin, SearchData.RoomCountMax,
                SearchData.FloorMin, SearchData.FloorMax, SearchData.EstateType == EstateType.House.GetEnumName() ? "domy" : "mieszkania");
            return uri;
        }

        private string ComposeSecondaryMarketUri()
        {
            const string secondaryMarketUri =
                "http://www.domiporta.pl/{0}-{1}?Localization={2}&PageNumber={3}&Surface.From={4}&Surface.To={5}&Price.From={6}&Price.To={7}&Rooms.From={8}&Rooms.To={9}&KeyWords={10}&RokBudowy.From={11}&RokBudowy.To={12}&Pietro.From={13}&Pietro.To={14}&PricePerMeter.From={15}&PricePerMeter.To={16}{17}{18}{19}{20}";

            var marketPart = SearchData.MarketType == MarketType.Secondary ? "&Rynek=Wtorny" : "";
            var photoPart = SearchData.OnlyWithPhoto ? "&OnlyWithPictures=True" : "";
            var sortingOrderPart = SearchData.OrderBy == String.Empty
                                       ? String.Empty
                                       : "&SortingOrder=" + SearchData.OrderBy;
            var sortingDirectionPart = "&SortingDirection=" + MapSortingDirection(SearchData.SortingDirection);
            var uri = String.Format(secondaryMarketUri, SearchData.AdvertisementType, SearchData.EstateType,
                SearchData.Localization, SearchData.Page, SearchData.AreaMin, SearchData.AreaMax, SearchData.PriceMin,
                SearchData.PriceMax, SearchData.RoomCountMin, SearchData.RoomCountMax, SearchData.Keywords,
                SearchData.YearBuiltMin, SearchData.YearBuiltMax, SearchData.FloorMin, SearchData.FloorMax,
                SearchData.PricePerMeterMin, SearchData.PricePerMeterMax, marketPart, photoPart, sortingOrderPart,
                sortingDirectionPart);
            return uri;
        }

        public override string Name
        {
            get { return "DomiportaPl"; }
        }

        protected override IParseResult<MyAdvertisement> ParseResponse(HttpWebResponse response, IAdvertisementSource<MyAdvertisement> website)
        {
            var list = new List<MyAdvertisement>();
            IParseResult<MyAdvertisement> result = new ParseResult<MyAdvertisement>();
            if (SearchData.MarketType == MarketType.Primary)
            {
                if (SearchData.EstateType == EstateType.House.GetEnumName() || SearchData.EstateType == EstateType.Flat.GetEnumName())
                    result = ParsePrimaryMarketResponse(response, list, website);
            }
            else
            {
                result = ParseSecondaryMarketResponse(response, list, website);
            }
            return result;
        }

        private ParseResult<MyAdvertisement> ParsePrimaryMarketResponse(HttpWebResponse response, List<MyAdvertisement> list, IAdvertisementSource<MyAdvertisement> website)
        {
            var dom = CQ.Create(response.GetResponseStream());
            var noResultsAlert = dom["div.InfoAlert div.AlertTekst:contains(Brak)"];
            if (noResultsAlert.Length > 0) return new ParseResult<MyAdvertisement>();
            var availableString = dom[".LiczbaOfertTekst strong:eq(0)"].Text().RemoveWhitespaces();
            var parsedAvailable = availableString.ToNullableLong();
            var allAvailable = parsedAvailable.HasValue ? parsedAvailable.Value : 0;
            var ads = dom["div.ListaWynikow div.WynikiElement"];
            foreach (var el in ads)
            {
                var ad = CQ.Create(el);
                var title = ad["div.TematOferty a:first"];
                var link = title.Attr("href");
                var img = ad["div.ZdjecieOgloszenia a img"];
                var location = ad["div.TematOferty p:first"];
                var locationSplit = location.Text().Split(',');
                try
                {
                    var baseAd = new MyAdvertisement
                        {
                            Title = title.Text(),
                            Photo = img.Attr("src"),
                            AdSource = Name + ".png",
                            City = locationSplit[0].Trim(),
                            Modified = null,
                            Source = website
                        };
                    if (locationSplit.Length > 2)
                    {
                        baseAd.District = locationSplit[1].Trim();
                        baseAd.Street = locationSplit[2].Trim();
                    }
                    else if (locationSplit.Length == 2)
                    {
                        baseAd.Street = locationSplit[1].Trim();
                    }
                    var adsInside = ParsePrimaryMarketFullAd(link, baseAd);
                    list.AddRange(adsInside);
                }
                catch (Exception ex)
                {
                    log.Debug(String.Format("{0}: exception while parsing [{1}]", GetType().Name, link), ex);
                }
            }
            return new ParseResult<MyAdvertisement> { AllAvailable = allAvailable, Results = list };
        }

        private ParseResult<MyAdvertisement> ParseSecondaryMarketResponse(HttpWebResponse response, List<MyAdvertisement> list, IAdvertisementSource<MyAdvertisement> website)
        {
            var dom = CQ.Create(response.GetResponseStream());
            var availableString = dom[".WynikiListaTytul form h2"].Text().RemoveNonDigits();
            var parsedAvailable = availableString.ToNullableLong();
            var allAvailable = parsedAvailable.HasValue ? parsedAvailable.Value : 0;
            var ads = dom["div.WynikiListaStandard div.NoweWyniki"];
            foreach (var el in ads)
            {
                var myAd = new MyAdvertisement();
                try
                {
                    var ad = CQ.Create(el);
                    var title = ad["div.Tytul a"];
                    myAd.Link = title.Attr("href");
                    var img = ad["div.ZdjecieOgloszenia a img"];
                    if (!title.Attr("href").Contains("%2Fnowe%2F") && !title.Attr("href").Contains("/nowe/"))
                        ParseSecondaryMarketFullAd(title.Attr("href"), myAd);
                    myAd.Title = title.Text();
                    myAd.Photo = img.Attr("src").Contains("brakzdjec") ? null : img.Attr("src");
                    myAd.AdSource = Name + ".png";
                    myAd.Source = website;
                    var date = ad["div.Data span.DataDodania"].Remove("h5").Text();
                    if (date.Contains("podbite")) date = date.RemoveFirstAtTheBeginning("podbite").RemoveWhitespaces();
                    else if (date.Contains("dodano"))
                        date = date.RemoveFirstAtTheBeginning("dodano").RemoveWhitespaces();
                    else date = String.Empty;
                    myAd.Modified = String.IsNullOrEmpty(date)
                                        ? null
                                        : DateTime.ParseExact(date, "dd-MM-yyyy", null)
                                                  .ToShortDateString()
                                                  .ToNullableDateTime();
                    list.Add(myAd);
                }
                catch (Exception ex)
                {
                    log.Debug(String.Format("{0}: exception while parsing [{1}]", GetType().Name, myAd.Link), ex);
                }
            }
            return new ParseResult<MyAdvertisement> { AllAvailable = allAvailable, Results = list };
        }

        private IEnumerable<MyAdvertisement> ParsePrimaryMarketFullAd(string link, MyAdvertisement baseAd)
        {
            var innerRequest = WebRequest.Create(LinkPrefix + link) as HttpWebRequest;
            var innerResponse = innerRequest.GetResponse() as HttpWebResponse;
            var innerDom = CQ.Create(innerResponse.GetResponseStream());
            var adRows = innerDom["#offers_37 div.DaneOgloszeniaElementRamka:not(.PaginacjaLista)"];
            var result = new List<MyAdvertisement>();
            foreach (var row in adRows)
            {
                var cq = row.Cq();
                var myAd = new MyAdvertisement(baseAd);
                myAd.Title = baseAd.Title;
                myAd.Link = LinkPrefix + cq.Find("a").Attr("href");
                myAd.Photo = baseAd.Photo;
                myAd.AdSource = baseAd.AdSource;
                myAd.Voivodship = baseAd.Voivodship;
                myAd.City = baseAd.City;
                myAd.Street = baseAd.Street;
                myAd.District = baseAd.District;
                var price = cq.Find("div.DaneOgloszeniaElementKolumna3").Text().RemoveWhitespaces();
                price = price.Substring(0, price.IndexOf("PLN") == -1 ? 0 : price.IndexOf("PLN"));
                myAd.Price = String.IsNullOrEmpty(price) ? null : price.ToNullableDecimal();
                var area = cq.Find("div.DaneOgloszeniaElementKolumna1").Text().RemoveWhitespaces().Replace(",", ".").RemoveFirstAtTheEnd("m");
                myAd.Area = String.IsNullOrEmpty(area) ? null : area.ToNullableDecimal();
                var rooms = cq.Find("div.DaneOgloszeniaElementKolumna2").Text().RemoveWhitespaces();
                myAd.RoomCount = String.IsNullOrEmpty(rooms) ? null : rooms.ToNullableInt();
                var floor = cq.Find("div.DaneOgloszeniaElementKolumna4").Text().RemoveWhitespaces();
                if (floor == "Parter")
                {
                    myAd.Floor = 0;
                }
                else
                {
                    myAd.Floor = String.IsNullOrEmpty(floor) ? null : floor.ToNullableInt();
                }
                result.Add(myAd);
                int priceNum;
                float areaNum;
                if (int.TryParse(price, out priceNum) && float.TryParse(area, out areaNum))
                {
                    myAd.PricePerMeter = Math.Round(priceNum / areaNum, 2).ToString().ToNullableDecimal();
                }
                else
                {
                    myAd.PricePerMeter = null;
                }
                myAd.YearBuilt = null;
            }
            return result;
        }

        private void ParseSecondaryMarketFullAd(string link, MyAdvertisement myAd)
        {
            var innerRequest = WebRequest.Create(link) as HttpWebRequest;
            var innerResponse = innerRequest.GetResponse() as HttpWebResponse;
            var innerDom = CQ.Create(innerResponse.GetResponseStream());
            var detailsTable = innerDom["div.InformacjePodstawoweDane"];
            var location = detailsTable.Find("div.InformacjePodstawoweDaneElement:last").Find("div.Opis").Text();
            var floor = detailsTable.Find("div.InformacjePodstawoweDaneElement div.Tytul:contains(piętro:)").Next("div.Opis").Text();
            var rooms = detailsTable.Find("div.InformacjePodstawoweDaneElement div.Tytul:contains(liczba pokoi:)").Next("div.Opis").Text();
            var year = detailsTable.Find("div.InformacjePodstawoweDaneElement div.Tytul:contains(rok budowy:)").Next("div.Opis").Text();
            var area = detailsTable.Find("div.InformacjePodstawoweDaneElement div.Tytul:contains(powierzchnia całkowita:)").Next("div.Opis").Text();
            ParseLocation(location, myAd);
            if (floor == "parter") myAd.Floor = 0;
            else myAd.Floor = String.IsNullOrEmpty(floor) ? null : floor.ToNullableInt();
            myAd.RoomCount = String.IsNullOrEmpty(rooms) ? null : rooms.ToNullableInt();
            myAd.YearBuilt = String.IsNullOrEmpty(year) ? null : year.ToNullableInt();
            myAd.Area = String.IsNullOrEmpty(area.Split(' ')[0]) ? null : area.Split(' ')[0].ToNullableDecimal();
            var pricesDiv = innerDom["#DetaleTytul div.CenaMetraStyl"];
            var priceText = pricesDiv.Find("div.Cena span").Text().RemoveWhitespaces();
            myAd.Price = String.IsNullOrEmpty(priceText) ? null : priceText.ToNullableDecimal();
            var pricePerMeterTxt = pricesDiv.Find("div.CenaMetra").Text().RemoveWhitespaces();
            pricePerMeterTxt = pricePerMeterTxt.Substring(0, pricePerMeterTxt.IndexOf("zł") == -1 ? 0 : pricePerMeterTxt.IndexOf("zł"));
            myAd.PricePerMeter = String.IsNullOrEmpty(pricePerMeterTxt) ? null : pricePerMeterTxt.ToNullableDecimal();
        }

        private void ParseLocation(string location, MyAdvertisement myAd)
        {
            var splitLocation = location.Split(',');
            var locationDictionary = splitLocation.Where(str => str.Contains(":")).Select(str => str.Split(':'))
                .ToDictionary(splitStr => splitStr[0].Trim(), splitStr => splitStr[1].Trim());
            foreach (var entry in locationDictionary)
            {
                if (entry.Key.Contains("województwo")) myAd.Voivodship = entry.Value;
                if (entry.Key.Contains("miejscowość")) myAd.City = entry.Value;
                if (entry.Key.Contains("ulica")) myAd.Street = entry.Value;
            }
        }

        private string MapSortingDirection(AdAggregator.SearchData.SortingDirection sortingDirection)
        {
            if (sortingDirection == AdAggregator.SearchData.SortingDirection.ASC) return "Ascending";
            if (sortingDirection == AdAggregator.SearchData.SortingDirection.DESC) return "Descending";
            return String.Empty;
        }
    }
}